home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Magnum One
/
Magnum One (Mid-American Digital) (Disc Manufacturing).iso
/
d18
/
tpu6.arc
/
TPU6AMS.PAS
< prev
next >
Wrap
Pascal/Delphi Source File
|
1991-07-04
|
22KB
|
596 lines
{$D+,O+,S+,R-,L+}
Unit TPU6AMS;
(*****************)
(**) INTERFACE (**) USES TPU6EQU, Dos;
(*****************)
TYPE
RngB = 0..65534;
RngW = 0..32766;
AryB = ARRAY[rngb] OF Byte;
AryW = ARRAY[rngw] OF Word;
SrcNam = _FileSpec;
HdrAry = ARRAY[0..3] OF Char;
LL = Word; { Local Scope Pointers (offsets) }
LG = RECORD { Global Scope Pointers to Other Units }
UntLL : LL; { Local to containing unit }
UntId : LL; { Local to external unit }
END;
{ The following Record is the Header and Locator for a Unit File } {.CP28}
UnitPtr = ^UnitHeader;
UnitHeader = RECORD
UHEYE : HdrAry; { +00 : = 'TPU9' }
UHxxx : HdrAry; { +04 : = $00000000 }
UHUDH : LL; { +08 : to Dictionary Head-This Unit }
UHIHT : LL; { +0A : to Interface Hash Header }
UHPMT : LL; { +0C : to PROC Map }
UHCMT : LL; { +0E : to CSeg Map }
UHTMT : LL; { +10 : to DSeg Map-Typed CONST's }
UHDMT : LL; { +12 : to DSeg Map-GLOBAL Variables }
UHxxy : LL; { +14 : purpose unknown }
UHLDU : LL; { +16 : to Donor Unit List }
UHLSF : LL; { +18 : to Source File List }
UHDBT : LL; { +1A : DEBUG Trace Table }
UHENC : LL; { +1C : to end non-code part of Unit }
UHZCS : Word; { +1E : CSEG Size-Aggregate }
UHZDT : Word; { +20 : DSEG Size-Typed CONSTS Only }
UHZFA : Word; { +22 : Fix-Up Size (CSegs) }
UHZFT : Word; { +24 : Fix-Up Size (Typed CONST's) }
UHZFV : Word; { +26 : DSEG Size for Global VARs }
UHDHT : LL; { +28 : to Global Hash Header }
UHSOV : Word; { +2A : Overlay Controls }
UHPad : ARRAY[0..9]
OF Word; { +2C : Reserved for Future Expansion ? }
END; { UnitHeader }
{ The Records below provide access to the PROC Map } {.CP12}
PMapRecPtr = ^PMapRec;
PMapRec = RECORD
ProcWd1,
ProcWd2 : Word; { function of these words unknown }
CSegOfs : Word; { offset within CSeg Map; $FFFF if null }
CSegJmp : Word; { offset to entry point; $FFFF if null }
END {PMapRec};
PMapPtr = ^PMapTab;
PMapTab = ARRAY[0..1] OF PMapRec; { model of PROC Map }
{ The Records below provide access to the CODE Map } {.CP12}
CMapRecPtr = ^CMapRec;
CMapRec = RECORD
CSegWd0 : Word; { purpose is unknown }
CSegCnt : Word; { byte count of module code }
CSegRel : Word; { byte count of module Relo List }
CSegTrc : Word; { Trace table offset or $FFFF }
END; {CMapRec}
CMapTabPtr = ^CMapTab;
CMapTab = ARRAY[0..1] OF CMapRec; { model of CSeg Map }
{ The Records below provide access to the CONST DSeg Map } {.cp12}
DMapRecPtr = ^DMapRec;
DMapRec = RECORD
DSegWd0 : Word; { purpose is unknown }
DSegCnt : Word; { byte count of data block }
DSegRel : Word; { byte count of data Relo List }
DSegOwn : LL; { To owner scope }
END; {DMapRec}
DMapTabPtr = ^DMapTab;
DMapTab = ARRAY[0..1] OF DMapRec; { model of DSeg Map }
{ The Record below is one entry in the Fix-Up List } {.CP13}
FixUpRecPtr = ^FixUpRec;
FixUpRec = RECORD
FixDnr : Byte; { Donor Unit Offset }
FixFlg : Byte; { Entry Format Flag }
FixWd1 : Word; { Offset to Map Table }
FixWd2 : Word; { Effective Address Adjuster }
FixOfs : Word; { offset to patch point in code/data block }
END; {FixUpRec}
FixUpPtr = ^FixUpList;
FixUpList = ARRAY[0..1] OF FixUpRec; { model of Fix-Up List }
{ The Record below maps the Dictionary Header in Turbo Units } {.CP08}
DNamePtr = ^ DNameRec;
DNameRec = RECORD
HLink : LL; { Hash Chain Link; Resolves Collisions }
DForm : Char; { Symbol Type; See StubRecord for types}
DSymb : _LexName; { Worst-Case Symbol Size (UPPER-CASE) }
END; {DNameRec}
{ The Record Below maps the Dictionary Stubs in Turbo Units } {.CP10}
DStubPtr = ^ DStubRcd;
DStubRcd = RECORD
CASE Char OF
'P': ( { --- For Untyped Constants --- }
sPTD : LG; { to type descriptor }
sPV1 : Word; { value of constant - LO Word }
sPV2 : Word); { (size varies) - HI Word }
'Y': ( { ----- For UNIT Entries ------ } {.CP05}
sYW1 : Word; { unknown use; normally zero }
sYCS : Word; { Speculate Signature Word }
sYNU : LL; { to next Unit in List (SUCC) }
sYPU : LL); { to prior Unit in List (PRED) }
'O', { ---- Label Declaratives ----- } {.CP05}
'T', { ---- Standard Procedures ---- }
'U', { ---- Standard Functions ---- }
'V': ( { ---- Standard "NEW" F/P ---- }
sVxx : Word); { semantics not precisely known }
'W': ( { ------- Standard Ports ------ } {.CP02}
sWxx : Byte); { 0=Byte Array, 1=Word Array }
'Q', { -------- Named Types -------- } {.CP03}
'X': ( { ----- External Variables ---- }
sQTD : LG); { to type descriptor }
'S': ( { ------ User Subprograms ----- } {.CP20}
sSTp : BYte; { 76543210 - Bit encoded }
{ .......1 = FAR Call Model }
{ ......1. = INLINE Declarative }
{ .....1.. = INTERRUPT Routine }
{ ....1... = .OBJ module code }
{ ...1.... = METHOD (Any) }
{ .011.... = Constructor METHOD }
{ .101.... = Destructor METHOD }
{ 1....... = ASSEMBLER attribute}
sSxx : Byte; { function unknown at present }
sSPM : Word; { Code byte count if INLINE, }
{ else, offset to PROC Map }
sSPS : LL; { to containing scope or zero }
sSHT : LL; { to local scope hash table }
sSVM : Word); { VMT Offset-VIRTUAL Method PTR }
{ Notes: "sSVM" is followed immediately by a Type }
{ Descriptor ($06). INLINE Declarative code }
{ Bytes then follow (if any). }
'R': ( { -- Variable, Field, Object -- } {.CP35}
sRAM : Byte; { allocation method codes: }
{ $00 = Global Variables in DS }
{ $01 = Typed Constants in DS }
{ $02 = VAR-BP based-Nested Scope }
{ $03 = Absolute[Segment:Offset] }
{ $06 = SELF Parameter-ADDR Stack }
{ $08 = Allocate in Record/Object }
{ $10 = Absolute Equivalence }
{ $22 = VALUE Parameter-BP based }
{ $26 = VAR Parameter-BP based }
sRVF : Longint; { See VarStub Below }
sRTD : LG); { to Type Descriptor }
END;
VarStubPtr = ^VarStub;
VarStub = RECORD
Case Byte Of { sRAM Byte in Type "R" Stub }
$02,$06,
$22,$26: (ROfs : Word; { allocation offset (BP) }
ROB : Word); { To Parent Scope/Zero }
$00,$01: (TOfs : Word; { allocation offset in map}
TOB : LL); { offset in VAR/CONST Map }
$03: (AOfs : Word; { Absolute Byte Offset }
ASeg : Word); { Absolute Segment Adr }
$08: (Bofs : Word; { Offset-Record Relative }
RChn : LL); { To Next Field/Method }
$10: (QLG : LG); { to Stub of Allocator }
End;
{ The Record below maps a Formal Parameter List Entry } {.CP08}
FormalParmRcd = RECORD
fPTD : LG; { to type descriptor for parameter }
fPAM : Byte; { passing model; 2=Value, 6=Address }
END;
InlineLst = ARRAY[0..1] OF Word; { model of INLINE code }
{ The Record below maps the Type Descriptors in Turbo Units } {.CP08}
TypePtr = ^TypeRecd;
TypeRecd = RECORD
tpTC : Byte; { Identifies the Variant Part }
tpTQ : Byte; { Type Qualifier }
tpSW : Word; { Storage Width in Bytes }
tpML : Word; { Next Method if tpTC=$06 }
CASE Byte OF {.CP04}
$00, { For NULL or Un-Typed Variables }
$0A, { For COMP,DOUBLE,EXTENDED,SINGLE }
$0B : (); { -------- For REAL Type -------- }
$01 : ( { ------ For ARRAY Types ------- } {.CP04}
BaseType : LG; { to TypeRecd for item arrayed }
BounDesc : LG; { to TypeRecd for array bounds }
);
$02 : ( { ------ For RECORD Types ------ } {.CP04}
RecdHash : LL; { to Hash Table for Field List }
RecdDict : LL; { to Field List Dictionary Begin }
);
$03 : ( { ------ For OBJECT Types ------ } {.CP15}
ObjtHash : LL; { to Fields & Methods Hash Table }
ObjtDict : LL; { to Fields & Methods Dictionary }
ObjtOwnr : LG; { to Parent Object Type Descript }
ObjtVMTs : Word;{ Size of VMT if Virtual Methods }
ObjtDMap : Word;{ Data Map Offset of VMT Template}
ObjtVMTO : Word;{ object instance offset to VMT }
{ pointer; $FFFF if object has }
{ no Virtual Methods (no VMT) }
ObjtName : LL; { to Object Dictionary Header }
ObjtRes0, { Usually $FFFF - Role Unknown }
ObjtRes1, { Usually zero - Role Unknown }
ObjtRes2, { Usually zero - Role Unknown }
ObjtRes3 : Word { Usually zero - Role Unknown }
);
$04, { ----- For FILE except TEXT ----} {.CP04}
$05: ( { ----- For TEXT file type ----- }
FileType : LG; { to TypeRecd for Base File Type }
);
$06: ( { ----- For Procedure Types ---- } {CP05}
PFRes : LG; { to Function Result TD / zero }
PNPrm : Word; { Formal Parameter Count/ zero }
PFPar : ARRAY[1..2] OF FormalParmRcd { model only}
);
$07 : ( { ------- For SET Types -------- } {.CP03}
SetBase : LG; { to base type descriptor of set }
);
$08 : ( { ----- For POINTER Types ------ } {.CP03}
PtrBase : LG; { to base type descriptor }
);
$09 : ( { ------ For STRING Types ------ } {.CP04}
StrBase : LG; { to SYSTEM.CHAR type descriptor }
StrBound : LG; { to array bounds for string typ }
);
$0C, { For BYTE,INTEGER,LONGINT,SMALLINT,WORD }{.CP15}
$0D, { ------- For BOOLEAN Type ------ }
$0E, { ------- For CHAR Type --------- }
$0F : ( { ---- For Enumerated Types ----- }
LoBnd : LongInt;{ lower bound of subrange }
HiBnd : LongInt;{ upper bound of subrange }
Cmpat : LG; { to upward compatible Type desc }
);
{ The Enumeration Type Descriptor is immediately }
{ followed by a SET Type Descriptor ($07) but we }
{ don't know what this achieves. Its base type }
{ LG points to the Enumerated Type Descriptor. }
END; { TypeRecd }
{ The Record below is a model Hash Table } {.CP07}
HashPtr = ^HashTable;
HashTable = RECORD
Bas : Word; { Base and Max Offset in Slt }
Slt : ARRAY[0..1] Of LL; { Slots in Hash Table }
END;
{ The Record below is an entry in the Unit Code/Data Donor List } {.CP07}
UDonorPtr = ^UDonorRec;
UDonorRec = RECORD
UDExxx : Word;
UDEnam : String[8]
END;
{ The Record below is an entry in the Source File List } {.CP10}
SrcFilePtr = ^SrcFileRec;
SrcFileRec = RECORD
SrcFlag : Byte; { 4=.PAS file, 3=.INC, 5=.OBJ }
SrcPad : Word; { no apparent use - always zero ? }
SrcTime : Word; { File Time Stamp if SrcFlag=3 or 4 }
SrcDate : Word; { File Date Stamp if SrcFlag=3 or 4 }
SrcName : SrcNam; { Varying length FileName.Extn }
END;
{ The Record below is an entry in the Trace Table } {.CP12}
TraceRecPtr = ^TraceRec;
TraceRec = RECORD
TrName : LL; { to Directory Entry of Proc/Method }
TrFill : Word; { to proc source file }
TrPfx : Word; { bytes of data in front of code }
TrBeg : Word; { Line Number of BEGIN Stmt }
TrLNos : Word; { Lines of Code to Execute in TRACE }
TrExec : ARRAY[1..2] { Model Array of bytes that map each }
OF Byte; { line of code to be traced by DEBUG }
END;
BufPtr = ^Buffer; {.CP06}
Buffer = RECORD { General Buffer Mapping }
CASE Boolean OF
True :( BufByt : AryB); { Byte Array over Buffer }
False:( BufWrd : AryW); { Word Array over Buffer }
END;
FUNCTION PtrAdjust(Arg: Pointer; Adj: Word): Pointer; {.CP22}
FUNCTION FormLL(Base,Ceil: Pointer): LL;
FUNCTION IsSystemUnit(U: UnitPtr): Boolean;
FUNCTION AddrStub(arg: DNamePtr): DStubPtr;
FUNCTION AddrHash(U: UnitPtr; Hash: LL): HashPtr;
FUNCTION AddrDict(U: UnitPtr; Hash: LL): DNamePtr;
FUNCTION AddrType(U: UnitPtr; TypeLG: LG): TypePtr;
FUNCTION AddrProcType(S: DStubPtr): TypePtr;
FUNCTION AddrNxtSrc(U: UnitPtr; Arg: SrcFilePtr): SrcFilePtr;
FUNCTION AddrSrcTabOff(U: UnitPtr; Offset: Word): SrcFilePtr;
FUNCTION CountPMapSlots(U: UnitPtr): Integer;
FUNCTION AddrPMapTab(U: UnitPtr): PMapPtr;
FUNCTION CountCMapSlots(U: UnitPtr): Integer;
FUNCTION AddrCMapTab(U: UnitPtr): CMapTabPtr;
FUNCTION CountDMapSlots(U: UnitPtr): Integer;
FUNCTION AddrDMapTab(U: UnitPtr): DMapTabPtr;
FUNCTION AddrTraceTab(U: UnitPtr): TraceRecPtr;
FUNCTION GetTrExecSize(T: TraceRecPtr): Integer;
FUNCTION AddrNxtTrace(U: UnitPtr; T: TraceRecPtr): TraceRecPtr;
FUNCTION AddrFixUps(U: UnitPtr): FixUpPtr;
FUNCTION AddrLGUnit(U: UnitPtr; TypeLG: LG): DNamePtr;
FUNCTION Public(Arg: Char) : Char;
(**********************) {.CP03}
(**) IMPLEMENTATION (**)
(**********************)
{ Function Below Converts PRIVATE Names to PUBLIC } {.CP04}
FUNCTION Public(Arg: Char): Char;
BEGIN Public := Chr(Ord(Arg) AND $7F) END;
{ Procedure Below Traps Pointer Violations } {.CP10}
PROCEDURE CheckPtrs(U, V: Pointer);
BEGIN
IF (U = Nil) OR (V = Nil) OR (Seg(U^) <> Seg(V^)) THEN
BEGIN
WriteLn('Pointer Violation in CheckPtrs');
Halt(1)
END
END; {CheckPtrs}
{ Function Below Computes an LL from two Pointers } {.CP09}
FUNCTION FormLL(Base, Ceil: Pointer): LL;
BEGIN
CheckPtrs(Base,Ceil);
IF Ofs(Base^) > Ofs(Ceil^)
THEN FormLL := LL(Ofs(Base^)-Ofs(Ceil^))
ELSE FormLL := LL(Ofs(Ceil^)-Ofs(Base^));
END;
{ Function Below Adjusts Pointer Values by Offsets } {.CP04}
FUNCTION PtrAdjust(Arg: Pointer; Adj: Word): Pointer;
BEGIN PtrAdjust := Ptr(Seg(Arg^),Ofs(Arg^) + Adj) END;
{ Function Below Checks to See if Unit Name is "SYSTEM" }
FUNCTION IsSystemUnit(U: UnitPtr): Boolean;
BEGIN
IsSystemUnit := DNamePtr(Ptr(Seg(U^),Ofs(U^)+U^.UHUDH))^.DSymb = 'SYSTEM'
END;
{ Function Below Finds The Stub Belonging to a Dictionary Header } {.CP05}
FUNCTION AddrStub(Arg: DNamePtr): DStubPtr;
CONST PrefixSize = SizeOf(LL)+SizeOf(Char) + 1;
BEGIN AddrStub := PtrAdjust(Arg,PrefixSize + Ord(Arg^.DSymb[0])) END;
{ Function Below Gets Pointer to Hash Table } {.CP04}
FUNCTION AddrHash(U: UnitPtr; Hash: LL): HashPtr;
BEGIN AddrHash := HashPtr(PtrAdjust(U,Hash)) END;
{ Function Below Gets Pointer to Dictionary Entry using LL } {.CP04}
FUNCTION AddrDict(U: UnitPtr; Hash: LL): DNamePtr;
BEGIN AddrDict := DNamePtr(PtrAdjust(U,Hash)) END;
{ Function Below Gets Pointer to Type Descriptor if Local to Unit } {.CP12}
FUNCTION AddrType(U: UnitPtr; TypeLG: LG): TypePtr;
VAR D:DNamePtr; S: DStubPtr; R: LL;
BEGIN
D := AddrDict(U,U^.UHUDH); {point to our unit DE}
S := AddrStub(D); {point to its stub }
R := FormLL(U,S); {get offset to stub }
IF R = TypeLG.UntId {if offset matches }
THEN AddrType := TypePtr(PtrAdjust(U,TypeLG.UntLL))
ELSE AddrType := Nil
END;
{ Function Below Gets Pointer to Unit Descriptor for Type via LG } {.CP21}
FUNCTION AddrLGUnit(U: UnitPtr; TypeLG: LG): DNamePtr;
VAR D: DNamePtr; S: DStubPtr; R: LL;
BEGIN
D := AddrDict(U,U^.UHUDH); {point to our unit hdr}
S := AddrStub(D); {point to our stub }
R := FormLL(U,S); {get offset to stub }
IF (R <> 0) THEN
IF (TypeLG.UntID <> R) THEN {if offsets don't match }
REPEAT
D := AddrDict(U,S^.sYNU); {chain to next DE}
IF D^.DForm <> 'Y' THEN R := 0 ELSE {if next is unit }
BEGIN
S := AddrStub(D); {its stub address}
R := FormLL(U,S); {and stub offset }
END;
UNTIL (R = TypeLG.UntID) OR (R = 0); {match of end list }
IF R <> 0 THEN AddrLGUnit := D {we had a match }
ELSE AddrLGUnit := Nil; {we couldn't find it}
END;
{ Function Below Gets Pointer to Procedure Stub Type Descriptor }{.CP04}
FUNCTION AddrProcType(S: DStubPtr): TypePtr;
BEGIN AddrProcType := TypePtr(PtrAdjust(@S^.sSVM,SizeOf(S^.sSVM))) END;
{ Function Below Gets Pointer to Next Entry in Source File List } {.CP21}
FUNCTION AddrNxtSrc(U: UnitPtr; Arg: SrcFilePtr): SrcFilePtr;
VAR J: LL; S: SrcFilePtr;
BEGIN
J := 0;
IF Arg = Nil THEN AddrNxtSrc := Nil ELSE
BEGIN
J := FormLL(U,Arg);
IF J < U^.UHLSF
THEN AddrNxtSrc := Nil ELSE
IF NOT (J < U^.UHDBT)
THEN AddrNxtSrc := Nil ELSE
BEGIN
S := SrcFilePtr(PtrAdjust(Arg,8 + Ord(Arg^.SrcName[0])));
IF FormLL(U,S) < U^.UHDBT
THEN AddrNxtSrc := S
ELSE AddrNxtSrc := Nil
END
END
END;
{ Function Below Gets Pointer to Source File List Entry at Offset }{.CP09}
FUNCTION AddrSrcTabOff(U: UnitPtr; Offset: Word): SrcFilePtr;
BEGIN
WITH U^ DO
IF (UHLSF+Offset) < UHDBT
THEN AddrSrcTabOff := SrcFilePtr(PtrAdjust(U,UHLSF+Offset))
ELSE AddrSrcTabOff := Nil
END;
{ Function Counts Number of Slots in PROC Map Table } {.CP06}
FUNCTION CountPMapSlots(U: UnitPtr): Integer;
BEGIN
CountPMapSlots := (U^.UHCMT-U^.UHPMT) DIV SizeOf(PMapRec);
END;
{ Function Gets Address of PROC Map Table } {.CP08}
FUNCTION AddrPMapTab(U: UnitPtr): PMapPtr;
BEGIN
IF CountPMapSlots(U) > 0
THEN AddrPMapTab := PMapPtr(PtrAdjust(U,U^.UHPMT))
ELSE AddrPMapTab := Nil
END;
{ Function Counts Number of Slots in CSeg Map Table } {.CP06}
FUNCTION CountCMapSlots(U: UnitPtr): Integer;
BEGIN
WITH U^ DO CountCMapSlots := (UHTMT-UHCMT) DIV SizeOf(CMapRec);
END;
{ Function Gets Address of CSeg Map Table } {.CP08}
FUNCTION AddrCMapTab(U: UnitPtr): CMapTabPtr;
BEGIN
IF CountCmapSlots(U) > 0
THEN AddrCMapTab := CMapTabPtr(PtrAdjust(U,U^.UHCMT))
ELSE AddrCMapTab := Nil
END;
{ Function Counts Number of DSeg Map Slots } {.CP06}
FUNCTION CountDMapSlots(U: UnitPtr): Integer;
BEGIN
WITH U^ DO CountDMapSlots := (UHDMT - UHTMT) DIV SizeOf(DMapRec)
END;
{ Function Gets Address of DSeg Map Table } {.CP08}
FUNCTION AddrDMapTab(U: UnitPtr): DMapTabPtr;
BEGIN
IF CountDMapSlots(U) > 0
THEN AddrDMapTab := DMapTabPtr(PtrAdjust(U,U^.UHTMT))
ELSE AddrDMapTab := Nil
END;
{ Function Below Gets Pointer to 1st Trace Table Entry or Nil } {.CP08}
FUNCTION AddrTraceTab(U: UnitPtr): TraceRecPtr;
BEGIN
IF U^.UHDBT = U^.UHENC
THEN AddrTraceTab := Nil
ELSE AddrTraceTab := TraceRecPtr(PtrAdjust(U,U^.UHDBT))
END; {AddrTraceTab}
{ Function Below Gets Byte Count in TrExec Array } {.CP20}
FUNCTION GetTrExecSize(T: TraceRecPtr): Integer;
VAR i,k : Integer;
BEGIN
IF T = Nil THEN GetTrExecSize := 0 ELSE
BEGIN
k := T^.TrLNos; {number of lines in array}
i := 1; {prime scan line number }
WHILE i <= k DO BEGIN {still have lines to test}
IF T^.TrExec[i] = $80 THEN {if "escape byte" present}
BEGIN
Inc(k); {bump array limit }
Inc(i) {bump to byte count slot }
END;
Inc(i) {check next slot }
END;
GetTrExecSize := k; {final byte count }
END;
END;
{ Function Below Gets Pointer to next Trace Table Entry or Nil } {.CP14}
FUNCTION AddrNxtTrace(U: UnitPtr; T: TraceRecPtr): TraceRecPtr;
VAR k : Integer;
BEGIN
IF T = Nil THEN AddrNxtTrace := Nil ELSE
BEGIN
k := GetTrExecSize(T);
T := TraceRecPtr(PtrAdjust(@T^.TrExec[1],LL(k)));
IF FormLL(U,T) >= U^.UHENC
THEN AddrNxtTrace := Nil
ELSE AddrNxtTrace := T
END
END; {AddrNxtTrace}
{ Function Below Gets Pointer to 1st Fixup Table Entry or Nil } {.CP13}
FUNCTION AddrFixUps(U: UnitPtr): FixUpPtr;
VAR j : Word;
BEGIN
IF U^.UHZFA = 0 THEN AddrFixUps := Nil ELSE
WITH U^ DO BEGIN
j := (UHENC + $F) AND $FFF0;
j := (UHZCS + $F) AND $FFF0 + j;
j := (UHZDT + $F) AND $FFF0 + j;
AddrFixUps := Ptr(Seg(U^),Ofs(U^) + j)
END
END; {AddrFixUps}
END.